//
//  LogManager.m
//  Pennyworth
//
//  Created by Chris Karr on 1/1/08.
//  Copyright 2008 Chris J. Karr. All rights reserved.
//

#import "LogManager.h"
#import "Observation.h"

#define LOG_PATH [NSString stringWithFormat:@"%@/Library/Application Support/Pennyworth/Logs/Log.log", NSHomeDirectory ()]
#define OBSERVATIONS @"Observations"

#define SENSOR @"Sensor"
#define OBSERVATION @"Observation"

#define LOGS @"Logs"
#define LOG_INTERVAL 60
#define DATE_FORMAT @"yyyy-MM-dd"
#define TIME_FORMAT @"hh:mm:ss"
#define LOG_SCREEN @"Log Screen"

#define ENABLE_SCREENSHOTS @"Enable Screenshots"

@implementation LogManager

@synthesize root;
@synthesize logDict;
@synthesize paused;
@synthesize pauseTimer;
@synthesize logTimer;

- (NSImage *) imageWithScreenShotInRect:(NSRect)cocoaRect
{
	PicHandle picHandle;
	GDHandle mainDevice;
	Rect rect;
	NSImage *image;
	NSImageRep *imageRep;
	
	// Convert NSRect to Rect
	SetRect (&rect, NSMinX (cocoaRect), NSMinY (cocoaRect), NSMaxX (cocoaRect), NSMaxY (cocoaRect));
	
	// Get the main screen. I may want to add support for multiple screens later
	mainDevice = GetMainDevice ();
	
	// Capture the screen into the PicHandle.
	picHandle = OpenPicture (&rect);
	CopyBits ((BitMap *)* (**mainDevice).gdPMap, (BitMap *)* (**mainDevice).gdPMap, &rect, &rect, srcCopy, 0l);
	ClosePicture();
	
	// Convert the PicHandle into an NSImage
	// First lock the PicHandle so it doesn't move in memory while we copy
	HLock((Handle)picHandle);
	imageRep = [NSPICTImageRep imageRepWithData:[NSData dataWithBytes:(*picHandle)
					length:GetHandleSize((Handle)picHandle)]];
	HUnlock((Handle)picHandle);
	
	// We can release the PicHandle now that we're done with it
	KillPicture(picHandle);
	
	// Create an image with the representation
	image = [[[NSImage alloc] initWithSize:[imageRep size]] autorelease];
	[image addRepresentation:imageRep];
	
	return image;
}

 - (NSData *) dataForScreen:(NSScreen *) screen
{
	NSRect rect = [screen frame];
	
	rect.origin.y = [[NSScreen mainScreen] frame].size.height - rect.size.height;

	NSImage * image = [self imageWithScreenShotInRect:rect];

	NSData * imageData = [image TIFFRepresentation];
	
	NSBitmapImageRep * imageRep = [NSBitmapImageRep imageRepWithData:imageData];
	NSDictionary * imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:0.5] forKey:NSImageCompressionFactor];
	imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];

	return imageData;
}

- (NSString *) logScreen
{
	NSData * jpg = [self dataForScreen:[NSScreen mainScreen]];
	
	NSString * imgPath = [NSString stringWithFormat:@"%@/%@.jpg", [LOG_PATH stringByDeletingLastPathComponent], [[NSDate date] description]];

	[jpg writeToFile:imgPath atomically:YES];

	return imgPath;
}

- (void) refresh
{
	[NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehavior10_4];
	NSDateFormatter * timeFormatter = [[NSDateFormatter alloc] init];
	[timeFormatter setDateFormat:TIME_FORMAT];

	NSMutableArray * childArray = [self.root mutableChildNodes];

	[childArray removeAllObjects];

	for (NSString * day in [[logDict allKeys] sortedArrayUsingSelector:@selector(compare:)])
	{
		NSDictionary * dayLog = [logDict valueForKey:day];
		
		NSTreeNode * dayNode = [NSTreeNode treeNodeWithRepresentedObject:[NSDictionary dictionaryWithObjectsAndKeys:day, @"title", nil]];
		[childArray addObject:dayNode];

		for (NSDictionary * eachLog in [dayLog valueForKey:OBSERVATIONS])
		{
			NSMutableDictionary * mutableEachLog = [NSMutableDictionary dictionaryWithDictionary:eachLog];
			[mutableEachLog setValue:[timeFormatter stringFromDate:[eachLog valueForKey:LOG_DATE]] forKey:@"title"];
			
			NSMutableArray * obs = [NSMutableArray array];
			
			for (NSString * key in [eachLog allKeys])
			{
				NSObject * observation = [eachLog valueForKey:key];

				if ([observation isKindOfClass:[NSArray class]])
				{
					NSMutableString * value = [NSMutableString stringWithString:@"[ "];
					
					NSArray * os = (NSArray *) observation;
					for (NSObject * o in os)
					{
						[value appendString:[o description]];
						[value appendString:@", "];
					}
					
					[value appendString:@"]"];
					observation = value;
				}
				
				[obs addObject:[NSDictionary dictionaryWithObjectsAndKeys:key, SENSOR, observation, OBSERVATION, nil]];
			}

			[mutableEachLog setValue:obs forKey:OBSERVATIONS];
			
			NSTreeNode * eachNode = [NSTreeNode treeNodeWithRepresentedObject:mutableEachLog];
										
			[[dayNode mutableChildNodes] addObject:eachNode];
		}
	}
	
	[timeFormatter release];
}

-(void) load
{
	NSFileManager * fm = [NSFileManager defaultManager];
	BOOL isDir = NO;
	
	[fm fileExistsAtPath:[LOG_PATH stringByDeletingLastPathComponent] isDirectory:&isDir];

	if (!isDir)
		[fm createDirectoryAtPath:[LOG_PATH stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];

	if ([fm fileExistsAtPath:LOG_PATH isDirectory:&isDir])
	{
		NSData * data = [NSData dataWithContentsOfFile:LOG_PATH];
		NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
			
		self.logDict = [NSMutableDictionary dictionaryWithDictionary:[unarchiver decodeObjectForKey:LOGS]];
			
		[unarchiver release];
	}
	else
		self.logDict = [NSMutableDictionary dictionary];
		
	[self refresh];
}

- (void) startLogging
{
	self.logTimer = [NSTimer scheduledTimerWithTimeInterval:LOG_INTERVAL target:self selector:@selector(update:) userInfo:nil repeats:YES];
	
	self.paused = [NSNumber numberWithBool:NO];
}
	
- (void) stopLogging
{
	[self.logTimer invalidate];
	self.logTimer = nil;
}

- (void) observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
	if ([keyPath isEqualToString:ENABLE_LOGGING])
	{
		if ([[NSUserDefaults standardUserDefaults] boolForKey:ENABLE_LOGGING])
			[self startLogging];
		else
			[self stopLogging];
	}
}
	
- (void) awakeFromNib
{
	[NSDateFormatter setDefaultFormatterBehavior:NSDateFormatterBehavior10_4];
	formatter = [[NSDateFormatter alloc] init];
	[formatter setDateFormat:DATE_FORMAT];
	
	self.root = [NSTreeNode treeNodeWithRepresentedObject:[NSDictionary dictionaryWithObjectsAndKeys:@"Saved Logs", @"title", nil]];
	
	[self load];

	if ([[NSUserDefaults standardUserDefaults] boolForKey:ENABLE_LOGGING])
		[self startLogging];
	
	[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:ENABLE_LOGGING options:0 context:NULL];
}


- (IBAction) pauseLogging:(id) sender
{
	self.paused = [NSNumber numberWithBool:YES];

	self.pauseTimer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(resumeLogging:) userInfo:nil repeats:NO];

	NSRunAlertPanel (@"Logging Paused", @"Logging is now paused. Logging will resume in 30 minutes.", @"OK", nil, nil);
}

- (void) resumeLogging:(NSTimer *) theTimer
{
	NSRunAlertPanel (@"Logging Resumed", @"Logging will now resume.", @"OK", nil, nil);
	
	self.paused = [NSNumber numberWithBool:NO];
	self.pauseTimer = nil;
}

- (void) update:(NSNotification *) note
{
	if ([paused boolValue])
		return;
	
	if (![[NSUserDefaults standardUserDefaults] boolForKey:ENABLE_LOGGING])
		return;
		
	[[NSNotificationCenter defaultCenter] postNotificationName:LOG_PREDICTIONS object:self];
	
	NSMutableDictionary * dict = [NSMutableDictionary dictionary];
	
	for (Observation * obs in [observations arrangedObjects])
		[dict setValue:obs.observation forKey:obs.sensor];
		
	[dict setValue:[NSDate date] forKey:LOG_DATE];

	if ([[NSUserDefaults standardUserDefaults] boolForKey:ENABLE_SCREENSHOTS])
		[dict setValue:[self logScreen] forKey:LOG_SCREEN];

	NSMutableDictionary * log = [logDict valueForKey:[formatter stringFromDate:[NSDate date]]];
	
	if (log == nil)
	{
		log = [NSMutableDictionary dictionary];
		[logDict setValue:log forKey:[formatter stringFromDate:[NSDate date]]];
	}
	
	NSMutableArray * obsArray = [log valueForKey:OBSERVATIONS];

	if (obsArray == nil)
	{
		obsArray = [NSMutableArray array];
		[log setValue:obsArray forKey:OBSERVATIONS];
	}
	
	[obsArray addObject:dict];
	
	NSMutableData * data = [NSMutableData data];
	
	NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
	[archiver encodeObject:logDict forKey:LOGS];
	[archiver finishEncoding];

	[data writeToFile:LOG_PATH atomically:YES];
	[archiver release];

	[[NSSound soundNamed:@"screen-grab"] play];

	[self refresh];
}

- (void) deleteNodeWithContent:(id) content tree:(NSTreeNode *) tree
{
	NSMutableArray * childs = [tree mutableChildNodes];
	
	NSMutableArray * deletes = [NSMutableArray array];
	
	for (NSTreeNode * node in childs)
	{
		if ([node representedObject] == content)
			[deletes addObject:node];
		
		[self deleteNodeWithContent:content tree:node];
	}
	
	[childs removeObjectsInArray:deletes];
}

- (IBAction) deleteLog:(id) sender
{
	if (selectedLog != nil)
	{
		NSDictionary * dict = [selectedLog content];
		
		[self deleteNodeWithContent:dict tree:self.root];
		
		for (NSDictionary * day in [logDict allValues])
		{
			NSDictionary * toDelete = nil;
			
			NSMutableArray * logs = [day valueForKey:OBSERVATIONS];
			
			for (NSDictionary * log in logs)
			{
				if ([[dict valueForKey:LOG_DATE] isEqual:[log valueForKey:LOG_DATE]])
					toDelete = log;
			}

			if (toDelete != nil)
				[logs removeObject:toDelete];
		}

		NSMutableData * data = [NSMutableData data];
	
		NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
		[archiver encodeObject:logDict forKey:LOGS];
		[archiver finishEncoding];
		[data writeToFile:LOG_PATH atomically:YES];
		[archiver release];
		
		[[NSFileManager defaultManager] removeFileAtPath:[dict valueForKey:LOG_SCREEN] handler:nil];
	}
}

- (IBAction) screenshot:(id) sender
{
	NSDictionary * object = [selectedLog content];

	NSImage * image = [[NSImage alloc] initWithContentsOfFile:[object valueForKey:LOG_SCREEN]];
	
	NSSize size = [image size];

	[image release];
	
	[screenPanel setContentSize:NSMakeSize(size.width / 2, size.height / 2)];
	[screenPanel setContentAspectRatio:size];
	[screenPanel setContentMaxSize:size];

	[screenPanel makeKeyAndOrderFront:sender];
}


@end
